Visualization | Social Network Analysis (ver.2.20260428)¶
Dr. Chan, Chun-Hsiang @ Department of Geography,
National Taiwan Normal University, Taipei, Taiwan
In [1]:
# import packages
import numpy as np
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
from pyvis import network as net
from jaal import Jaal
/Users/toodou/miniconda3/envs/toodou/lib/python3.10/site-packages/dash_bootstrap_components/_table.py:5: UserWarning: The dash_html_components package is deprecated. Please replace `import dash_html_components as html` with `from dash import html` import dash_html_components as html
Create a Random Graph¶
In [2]:
# create Erdos Renyi random graph
G = nx.erdos_renyi_graph(50, 0.2, seed=21, directed=False)
In [3]:
# set options
options = {
"font_size": 8,
"node_size": 100,
"node_color": "#A0CBE2",
"edge_color": "brown",
"linewidths": 0.1,
"width": 0.08,
}
In [4]:
# circular
plt.subplots(figsize=[12,6], dpi=100)
nx.draw_circular(G, **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()
In [5]:
# kamada kawai
plt.subplots(figsize=[12,6], dpi=100)
nx.draw_kamada_kawai(G, **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()
In [6]:
# shell
plt.subplots(figsize=[12,6], dpi=100)
nx.draw_shell(G, **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()
In [7]:
# spectral
plt.subplots(figsize=[12,6], dpi=100)
nx.draw_spectral(G, **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()
In [8]:
# spring
plt.subplots(figsize=[12,6], dpi=100)
nx.draw_spring(G, **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()
In [9]:
plt.subplots(figsize=[12,6], dpi=100)
nx.draw_random(G, **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()
Pyvis¶
In [11]:
# create vis network
visG = net.Network(notebook=True, height="800px", width="100%", bgcolor="#222222",
font_color="white", filter_menu=True, select_menu=True, cdn_resources='remote')
# import graph from networkx
visG.from_nx(G)
# show
visG.show_buttons() #filter_=['physics']
visG.show('pyvis_ex.html') # save file
pyvis_ex.html
Out[11]:
In [12]:
# extract networkx node to dataframe
G_nodes = pd.DataFrame.from_records(np.vstack(list(G.nodes)), columns=['ID'])
# add size with random values
G_nodes['importance'] = abs(np.random.normal(15, 12, G_nodes.shape[0]))
# add group with random values
G_nodes['group'] = np.random.randint(5, size=G_nodes.shape[0])+1
# add node label with random values
G_nodes['code'] = 'S_'+G_nodes['ID'].astype('str')
# preview data
G_nodes.head()
Out[12]:
| ID | importance | group | code | |
|---|---|---|---|---|
| 0 | 0 | 16.161601 | 2 | S_0 |
| 1 | 1 | 34.763198 | 2 | S_1 |
| 2 | 2 | 8.745326 | 5 | S_2 |
| 3 | 3 | 22.068353 | 1 | S_3 |
| 4 | 4 | 22.587786 | 4 | S_4 |
In [13]:
# extract networkx edge to dataframe
G_edges = pd.DataFrame.from_records(np.vstack(list(G.edges)), columns=['source','target'])
# add weight with random values
G_edges['weight'] = abs(np.random.normal(100, 25, G_edges.shape[0]))**2/1000
# preview data
G_edges.head()
Out[13]:
| source | target | weight | |
|---|---|---|---|
| 0 | 0 | 1 | 14.839815 |
| 1 | 0 | 11 | 8.733637 |
| 2 | 0 | 14 | 10.822140 |
| 3 | 0 | 18 | 8.210414 |
| 4 | 0 | 23 | 11.063825 |
In [14]:
color_map = {1:'#CCFF00', 2:'#CCCC33', 3:'#CC9966', 4:'#CC6699', 5:'#CC33CC', 6:'#CC00FF',
7:'#9900FF', 8:'#6600CC', 9:'#660066', 10:'#660033', 11:'#CC99FF', 12:'#FFCCFF',
13:'#FFFFCC', 14:'#FF3300', 15:'#333333', 16:'#666666'}
options = {
'edge_color': '#FFDEA2',
'width': .5,
'with_labels': False,
'font_weight': 'regular',
}
In [15]:
# initialize
g = net.Network(height="800px", width="100%", bgcolor="#222222", font_color="white",
filter_menu=True, select_menu=True, cdn_resources='remote')
# add node
for i in range(len(G_nodes['code'])):
g.add_node(int(G_nodes['ID'][i]), size=int(G_nodes['importance'][i]), label=G_nodes['code'][i],
group=int(G_nodes['group'][i]))
# add edge
for i in range(len(G_edges)):
g.add_edge(int(G_edges['source'][i]), int(G_edges['target'][i]), weight=G_edges['weight'][i])
# add options
g.set_template_dir = None
g.show_buttons()
# export
g.show('adv_ex.html', notebook=False)
adv_ex.html
Export¶
In [16]:
G_nodes.to_csv('ex_node.csv', index=False)
G_edges.to_csv('ex_edge.csv', index=False)
Jaal¶
In [17]:
# rename columns for Jaal
G_edges.columns = ['from', 'to', 'weight'] # Jaal reinforce source & target should be named as from and to
G_nodes.columns = ['id', 'importance', 'group', 'code'] # "size" is reserved word in Jaal
In [18]:
# change the data type of group column into String
G_nodes['group'] = G_nodes['group'].astype(str)
In [19]:
# compute the mean and standard deviation for division
mean = np.mean(G_edges['weight'])
stddev = np.std(G_edges['weight'], ddof=1)
# using value to define the division
G_edges['strength'] = 'Middle'
G_edges.loc[G_edges['weight'] > mean + 1*stddev, 'strength'] = 'Higher'
G_edges.loc[G_edges['weight'] < mean + 1*stddev, 'strength'] = 'Lower'
In [20]:
# simple demo
Jaal(G_edges, G_nodes).plot()
Parsing the data...Done
In [21]:
# set for directed graph
Jaal(G_edges, G_nodes).plot(directed=True)
Parsing the data...Done
No trigger
In [22]:
# init Jaal and run server
Jaal(G_edges, G_nodes).plot(vis_opts={'height': '600px', # change height
'interaction':{'hover': True}, # turn on-off the hover
'physics':{'stabilization':{'iterations': 100}}},
) # define the convergence iteration of network
Parsing the data...Done
No trigger No trigger inside color node group inside color edge strength Modifying node size using importance Modifying edge size using weight inside color node group inside color edge strength Modifying node size using importance Modifying edge size using weight inside color node group inside color edge strength Modifying node size using importance Modifying edge size using weight
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]: